1. Field of Invention
The present invention relates generally to improving the performance of virtual machines. More particularly, the present invention relates to rewriting bytecodes to substantially eliminate redundant checks on the state of a class and type checks.
2. Description of the Related Art
A conventional implementation of the Java™ programming language developed by Sun Microsystems, Inc. of Palo Alto, Calif., is a Java™ virtual machine which includes a byte-code interpreter. A conventional virtual machine reads in and executes bytecodes, as will be appreciated by those skilled in the art. When executing dynamically loaded classes, optimizations are often made to the bytecodes during execution in order to enable the bytecodes to execute faster the next time they are executed.
In some systems, Java™ classes may “preloaded” into read only memory (ROM) such that optimizations or rewrites to bytecodes are made statically before execution of a virtual machine begins. Within such systems, the runtime representation of a class that is used by the virtual machine is preloaded into ROM. For a class file to be preloaded, the class file is first processed by a tool, e.g. a preloader, that is capable of converting the class file into a runtime representation of the class that is suitable for the virtual machine. The preloader takes care of substantially any bytecode optimizations or rewrites that the virtual machine would typically perform at runtime for dynamically loaded classes.
Generally, a virtual machine is responsible for executing the “<clinit>” method of a Java™ class. The “<clinit>” method of a Java™ class is typically generated by a Java™ compiler to initiate static variables, and may be considered to be a static initializer. A static initializer is arranged to set aside memory to store a static variable. By way of example, a static initializer called with respect to a class “MyClass” may be arranged to set aside memory to store static variables associated with class MyClass. Specifically, class MyClass may be defined as follows:
Class MyClass {static int x = 5}For class MyClass, the static initializer generated by a compiler initializes a static variable to store the value “5.” That is, the static initializer initializes previously allocated memory to store the value associated with “x.”
FIG. 1 is a diagrammatic representation of a system which includes a preloader and a virtual machine. A system 110 includes a Java™ compiler 114, a preloader 118, a C compiler 122 which compiles files written in the C programming language, and a virtual machine 126. Compiler 114 takes as input a Java™ source file written in the Java™ programming language, e.g., MyClass.java, and compiles the file into a class file, e.g., MyClass.class. The class file is inputted to preloader 118, which may be a Java™ Code Compact developed by Sun Microsystems, Inc. of Palo Alto, Calif. Preloader 118 creates a source file, e.g., a ramjava.c file, for compiler 122, that contains data structures which are used by virtual machine 126. Preloader 118 also sets aside memory to store static variables. Compiler 122 compiles source files generated by preloader 118 into object files, e.g., a romjava.o file. The object files are provided to virtual machine 126 as preloaded files, along with standard virtual machine object files, e.g., a vm.o file, such that virtual machine 126 may execute the files.
A virtual machine typically executes the “<clinit” method of a Java™ class the first time an “active” reference is made to the class. An active reference is generally a reference which refers to a static variable. In addition, creating an instance of a class may be an example of an active reference. Active references are described in The Java™ Virtual Machine Specification by Tim Lindholm and Frank Yellin (ISBN 0-201-63452-X), which is incorporated herein by reference.
An active reference to class MyClass would cause “x” to be initialized to store the value “5.” In some cases, an active reference to a class may cause a call to a method of the class to be made. As such, each time an active reference to a class is made, a check of the class is substantially required to determine if the “<clinit>” method has already been run.
Due to performance issues, most Java™ virtual machines attempt to reduce the number of checks made to determine if the “<clinit>” method has been run more than once for a given bytecode. One standard method used to reduce the number of checks made involves modifying the bytecode used to make the active reference so that the next time the bytecode is executed, no check needs to be made. For example, the “new” bytecode may be changed to the “new—quick” bytecode. By changing the “new” bytecode to the “new—quick” bytecode, the need for the “new—quick” bytecode to check if the “<clinit>” method of the class has been run may be eliminated because it is known by the existence of the “new—quick” bytecode that the “<clinit>” has already been run.
However, in Java™ virtual machine environments where Java™ classes are preloaded into ROM, the bytecode that caused an active reference may not be modified. This means the check to see if the “<clinit>” method was run is made every time a bytecode that makes an active reference to a class is executed. For example, a preloaded class would already have all “new” bytecodes changed to “new—quick” opcodes. However, “new—quick” opcodes would substantially always have to check to see if the “<clinit>” method for a class has been run because “new—quick” is also used for the first active reference to the class. Such checks are generally time-consuming, and unnecessary checks often compromise the efficiency of the virtual machine.
Since checks for a “cinit” method during the runtime of a virtual machine are time-consuming and may be unnecessary, e.g., if a “cinit” method is known to have been executed, avoiding such checks when they are known to be unnecessary may increase the performance of the virtual machine. Avoiding other types of checks during runtime may also increase the performance of the virtual machine. For example, opcodes such as a getfield opcode or a putfield opcode, which get contents of a field from an object and place contents into a field of an object, respectively, may involve obtaining either scalar values or references. A garbage collector may use such commands since it requires knowledge of every reference in a system. However, the use of such commands requires a type check to determine if a field of an object holds a scalar value, e.g., an integer, or a reference. When the contents hold a reference, then a barrier may be implemented, and either a read barrier check or a write barrier check may be performed in the course of a garbage collection. If the contents of a particular field contain a scalar, then a barrier is not implemented, and read and write barrier checks are not performed. If it has previously been determined that the contents of a particular field pertain to a scalar, then always checking the type associated with the contents when a garbage collection is required may compromise the performance the runtime performance of a virtual machine, as such checks may be expensive.
Therefore, what is desired is a relatively inexpensive method and apparatus for reducing the number of runtime checks performed during the execution of a virtual machine. Specifically, what is needed is a method and apparatus for substantially eliminating unnecessary checks for a “<clinit>” method and unnecessary type checks.